package com.corner.model.cornerservice.utils;

import com.alibaba.fastjson.JSONObject;
import com.corner.model.cornerservice.domain.GeoPoint;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.Set;

public class ServiceQuery {
    public static final String PROPERTY = "property";
    public static final String FOREIGN_KEY = "foreignkey";

    public static final String $SELECT = "$select"; // select from user where id in ( select uid from info );
    public static final String $DONTSELECT = "$dontSelect";
    public static final String $EXISTS = "$exists"; // select * from user where avatar is not null;
    public static final String $NIN = "$nin";  //select * from user where id not in (1,2,3);
    public static final String $GT = "$gt";    // >
    public static final String $IN = "$in";    // select * from user where id in (1,2,3);
    public static final String $NE = "$ne";    // !=
    public static final String $GTE = "$gte"; // >=
    public static final String $LTE = "$lte"; // <=
    public static final String $LT = "$lt"; // <
    protected JSONObject query;
    private JSONObject property;
    private JSONObject foreignkey;
    private JSONObject where;
    private Set<String> orders;
    private Set<String> include;
    private Set<String> notInclude;

    public String toString() {
        try {
            return URLEncoder.encode(generateParamsJson().toJSONString(), "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected JSONObject generateParamsJson() {
        initQuery();
        if (property != null || foreignkey != null) {
            where = new JSONObject();
            if (property != null) {
                where.put(PROPERTY, property);
            }
            if (foreignkey != null) {
                where.put(FOREIGN_KEY, foreignkey);
            }
            query.put("where", where);
        }
        if (orders != null) {
            StringBuilder sb = new StringBuilder();
            for (String item : orders) {
                sb.append(item).append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            query.put("order", sb.toString());
        }
        if (include != null) {
            StringBuilder sb = new StringBuilder();
            for (String item : include) {
                sb.append(item).append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            query.put("include", sb.toString());
        } else if (notInclude != null) {
            StringBuilder sb = new StringBuilder();
            for (String item : notInclude) {
                sb.append("-").append(item).append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            query.put("include", sb.toString());
        }
        return query;
    }

    public void withIn(String key, GeoPoint value, Double distance) {
        initQuery();
        JSONObject wi = new JSONObject();
        wi.put("wiKey", key);
        wi.put("geoPoint", value);
        wi.put("distance", distance);
        query.put("wi", wi);
    }

    /**
     * near
     * @param key
     * @param value
     */
    public void near(String key, GeoPoint value) {
        initQuery();
        JSONObject near = new JSONObject();
        near.put("nearKey", key);
        near.put("geoPoint", value);
        query.put("near", near);
    }

    /**
     *  "ib"  InBorder
     * @param key
     * @param LB
     * @param RT
     */
    public void inBorder(String key, GeoPoint LB, GeoPoint RT) {
        initQuery();
        JSONObject ib = new JSONObject();
        ib.put("ibKey", key);
        ib.put("LB", LB);
        ib.put("RT", RT);
        query.put("ib", ib);
    }

    protected void initQuery() {
        if (query == null) {
            query = new JSONObject();
        }
    }

    public void setLimit(Integer limit) {
        initQuery();
        query.put("limit", limit);
    }

    public void setLastId(Integer lastId) {
        initQuery();
        query.put("lastId", lastId);
    }

    public void acenderBy(String acenderBy) {
        if (orders == null) {
            orders = new HashSet<>();
        }
        orders.add("+" + acenderBy);
    }

    public void descenderBy(String descenderBy) {
        if (orders == null) {
            orders = new HashSet<>();
        }
        orders.add("-" + descenderBy);
    }

    public void include(String field) {
        initInclude();
        include.add(field);
    }

    private void initInclude() {
        if (include == null) {
            include = new HashSet<String>();
        }
    }

    private void initNotInclude() {
        if (notInclude == null) {
            notInclude = new HashSet<String>();
        }
    }

    public void notInclude(String field) {
        initNotInclude();
        notInclude.add(field);
    }

    public void greaterThan(String key, Object value, String type) {
        putValue($GT, key, value, type);
    }

    public void greaterThan(String key, SubServiceQuery value, String type) {
        putValue($GT, key, value, type);
    }

    public void greaterAndEqualThan(String key, Object value, String type) {
        putValue($GT, key, value, type);
    }

    public void greaterAndEqualThan(String key, SubServiceQuery value,
                                    String type) {
        putValue($GT, key, value, type);
    }

    public void lessThan(String key, Object value, String type) {
        putValue($LT, key, value, type);
    }

    public void lessThan(String key, SubServiceQuery value, String type) {
        putValue($LT, key, value, type);
    }

    public void lessAndEqualThan(String key, Object value, String type) {
        putValue($LTE, key, value, type);
    }

    public void lessAndEqualThan(String key, SubServiceQuery value, String type) {
        putValue($LTE, key, value, type);
    }

    public void in(String key, Object[] value, String type) {
        putValue($IN, key, value, type);
    }

    public void in(String key, SubServiceQuery value, String type) {
        putValue($IN, key, value, type);
    }

    public void notIn(String key, Object[] value, String type) {
        putValue($NIN, key, value, type);
    }

    public void notIn(String key, SubServiceQuery value, String type) {
        putValue($NIN, key, value, type);
    }

    public void exist(String key, Boolean value, String type) {
        putValue($EXISTS, key, value, type);
    }

    public void equal(String key, SubServiceQuery value, String type) {
        putValue($SELECT, key, value, type);
    }

    public void equal(String key, Object value, String type) {
        switch (type) {
            case FOREIGN_KEY:
                JSONObject jsonObject = getForeignKey();
                jsonObject.put(key, value);
                break;
            case PROPERTY:
                jsonObject = getProperty();
                jsonObject.put(key, value);
                break;
        }
    }

    public void notEqual(String key, SubServiceQuery value, String type) {
        putValue($NE, key, value, type);
    }

    public void notEqual(String key, Object value, String type) {
        putValue($NE, key, value, type);
    }

    private void putValue(String opration, String key, Object value, String type) {
        switch (type) {
            case FOREIGN_KEY:
                JSONObject jsonObject = getForeignValueJson(key);
                jsonObject.put(opration, value);
                break;
            case PROPERTY:
                jsonObject = getPropertyValueJSON(key);
                jsonObject.put(opration, value);
                break;
        }
    }

    private void putValue(String opration, String key, SubServiceQuery value,
                          String type) {
        switch (type) {
            case FOREIGN_KEY:
                JSONObject jsonObject = getForeignValueJson(key);
                jsonObject.put(opration, value.generateParamsJson());
                break;
            case PROPERTY:
                jsonObject = getPropertyValueJSON(key);
                JSONObject subSelect = new JSONObject();
                subSelect.put($SELECT, value.generateParamsJson());
                jsonObject.put(opration, subSelect);
                break;
        }
    }

    /**
     * 获取property中对应key的值Json
     *
     * @param key
     * @return
     */
    private JSONObject getPropertyValueJSON(String key) {
        getProperty();
        JSONObject jsonObject = property.getJSONObject(key);
        if (jsonObject == null) {
            jsonObject = new JSONObject();
        }
        property.put(key, jsonObject);
        return jsonObject;
    }

    /**
     * 获取Foreign中对应key的值Json
     *
     * @param key
     * @return
     */
    private JSONObject getForeignValueJson(String key) {
        getForeignKey();
        JSONObject jsonObject = foreignkey.getJSONObject(key);
        if (jsonObject == null) {
            jsonObject = new JSONObject();
        }
        foreignkey.put(key, jsonObject);
        return jsonObject;
    }

    private JSONObject getForeignKey() {
        if (foreignkey == null) {
            foreignkey = new JSONObject();
        }
        return foreignkey;
    }

    private JSONObject getProperty() {
        if (property == null) {
            property = new JSONObject();
        }
        return property;
    }
}
